其實上一篇改好的 Dockerfile 已經滿足我們的最低需求了,但是在我們把它跑起來之前,還需要理解 entrypoint.sh
的設計,才能更好地掌握它的使用方式。
首先我們來分析官方的 entrypoint.sh
#!/bin/bash
set -e # 啟用錯誤即退出模式,如果有任何指令失敗,腳本將立即終止
if [ -f "$PASSWORD_FILE" ]; then # 如果 PASSWORD_FILE 檔案存在,從該文件中讀取密碼
PASSWORD="$(< $PASSWORD_FILE)"
fi
# 設定 PostgreSQL 資料庫的主機、埠口、用戶和密碼,從環境變數中讀取
: ${HOST:=${DB_PORT_5432_TCP_ADDR:='db'}}
: ${PORT:=${DB_PORT_5432_TCP_PORT:=5432}}
: ${USER:=${DB_ENV_POSTGRES_USER:=${POSTGRES_USER:='odoo'}}}
: ${PASSWORD:=${DB_ENV_POSTGRES_PASSWORD:=${POSTGRES_PASSWORD:='odoo'}}}
DB_ARGS=() # 建立資料庫連線參數字串
function check_config() {
param="$1"
value="$2"
# 如果設定文件中已經設置了參數,則從設定文件中讀取該參數的值
if grep -q -E "^\s*\b${param}\b\s*=" "$ODOO_RC" ; then
value=$(grep -E "^\s*\b${param}\b\s*=" "$ODOO_RC" | cut -d " " -f3 | sed 's/["\n\r]//g')
fi
DB_ARGS+=("--${param}") # 將參數名稱加入連線參數字串
DB_ARGS+=("${value}") # 將參數值加入連線參數字串
}
# 將 4 個參數名稱跟值分別丟入 function 中
check_config "db_host" "$HOST"
check_config "db_port" "$PORT"
check_config "db_user" "$USER"
check_config "db_password" "$PASSWORD"
# 根據傳入的參數決定如何啟動 odoo
case "$1" in
-- | odoo)
shift
if [[ "$1" == "scaffold" ]] ; then
exec odoo "$@" # 如果傳入的參數是 scaffold,直接執行 odoo scaffold 命令
else
wait-for-psql.py ${DB_ARGS[@]} --timeout=30 # 等待 PostgreSQL 就緒,然後啟動 odoo
exec odoo "$@" "${DB_ARGS[@]}"
fi
;;
-*)
wait-for-psql.py ${DB_ARGS[@]} --timeout=30 # 同樣等待資料庫準備就緒,然後啟動 odoo
exec odoo "$@" "${DB_ARGS[@]}"
;;
*)
exec "$@" # 如果傳入的不是 odoo 相關指令,則執行傳入的指令
esac
exit 1 # 如果沒有任何動作成功執行,腳本將以錯誤狀態退出
[CMD] 範例解釋:
如果傳入的指令是 odoo scaffold my_module
:
odoo scaffold my_module
,生成名為 my_module
的模組結構。如果傳入的指令是 odoo -d mydb
:
wait-for-psql.py
檢查資料庫是否就緒,然後執行 odoo -d mydb --db_host db --db_port 5432 --db_user odoo --db_password odoo
。如果傳入的是 bash
或其他指令:
bash
,這樣我們可以進入容器並操作。再來是 entrypoint.sh 裡面提到的 wait-for-psql.py
腳本主要用來在啟動 odoo 之前,檢查 PostgreSQL 資料庫是否已經就緒。它會根據傳入的資料庫參數進行連線測試,並且在指定的時間內重試,直到資料庫可以正常連接為止。
#!/usr/bin/env python3
import argparse
import psycopg2
import sys
import time
if __name__ == '__main__':
# 設定指令參數語法分析器
arg_parser = argparse.ArgumentParser()
arg_parser.add_argument('--db_host', required=True) # 資料庫主機
arg_parser.add_argument('--db_port', required=True) # 資料庫埠口
arg_parser.add_argument('--db_user', required=True) # 資料庫使用者
arg_parser.add_argument('--db_password', required=True) # 資料庫密碼
arg_parser.add_argument('--timeout', type=int, default=5) # 超時時間,預設為5秒
# 解析傳入的參數
args = arg_parser.parse_args()
start_time = time.time() # 紀錄開始時間
while (time.time() - start_time) < args.timeout:
try:
# 嘗試連接資料庫
conn = psycopg2.connect(user=args.db_user, host=args.db_host, port=args.db_port, password=args.db_password, dbname='postgres')
error = ''
break # 連接成功則跳出迴圈
except psycopg2.OperationalError as e:
error = e # 捕捉錯誤訊息
else:
conn.close() # 關閉連接
time.sleep(1) # 若連接失敗,等待 1 秒再重試
if error:
# 如果連接失敗,輸出錯誤訊息並退出
print("Database connection failure: %s" % error, file=sys.stderr)
sys.exit(1)
現在我們已經完整了解第四篇「從官方 Dockerfile 開始調整:以原始碼安裝取代套件安裝」最後一部分── ENTRYPOINT
和 CMD ["odoo"]
──的作用。當我們將 odoo
這個指令傳給 entrypoint
時,它會蒐集資料庫的參數,組成一個字串變數,並附加在 odoo
後面作為參數執行。
在我們介紹如何使用 docker-compose
前,先來看看如何不用它來手動啟動 odoo 和 PostgreSQL。
我們先啟動 PostgreSQL 資料庫:
docker run -d -v odoo-db:/var/lib/postgresql/data \
-e POSTGRES_USER=odoo \
-e POSTGRES_PASSWORD=odoo \
-e POSTGRES_DB=postgres \
--name db postgres:15
這條指令會啟動 PostgreSQL 資料庫,並將資料存放在 odoo-db
的 volume 中。
由於我們的 odoo 使用的是自訂的 Dockerfile,因此不能直接使用官方的映像檔來啟動 odoo。需要先構建我們本地的 Dockerfile:
docker build -t custom-odoo .
完成映像檔的構建後,接著啟動 odoo:
docker run -v odoo-data:/var/lib/odoo \
-d -p 8069:8069 \
--name odoo \
--link db:db \
custom-odoo
這條指令會啟動 odoo,並將資料存放在 odoo-data
的 volume 中。我們還使用了 --link db:db
來將 odoo 容器連接到我們之前啟動的 PostgreSQL 資料庫。
下一章,我們就來編寫 docker-compose.yml
,一次啟動 PostgreSQL 和 odoo 容器。